#include "fiber.h"
#include <atomic>
#include "config.h"
#include "macro.h"
#include "log.h"
#include "scheduler.h"
namespace sylar
{
    static std::atomic<uint64_t> s_fiber_id{0};
    static std::atomic<uint64_t> s_fiber_count{0};
    static Logger::ptr g_logger = SYLAR_LOG_NAME("system");

    static thread_local Fiber *t_fiber = nullptr;
    static thread_local std::shared_ptr<Fiber> t_threadFiber = nullptr;
    static ConfigVar<uint32_t>::ptr g_fiber_stack_size =
        Config::Lookup<uint32_t>("fiber.stack_size", 1024 * 1024, "fiber stack size");

    class MallocStackAllocator
    {
    public:
        static void *Alloc(size_t size)
        {
            return malloc(size);
        }
        static void Dealloc(void *vp, size_t size)
        {
            return free(vp);
        }
    };

    using StackAllocator = MallocStackAllocator;
    Fiber::Fiber()
    {
        SS() << "Fiber::Fiber() called";
        SetThis(this);
        m_state = EXEC;

        if (getcontext(&m_ctx))
        {
            SYLAR_ASSERT2(false, "Fiber::Fiber() getcontext()");
        }
        ++s_fiber_count;
        m_id = ++s_fiber_id;

        SS() << "Fiber::Fiber() main id = " << m_id;
    }
    Fiber::Fiber(std::function<void()> cb, size_t stacksize, bool use_caller) : m_id(++s_fiber_id), m_cb(cb)
    {

        SS() << "Fiber::Fiber(std::function<void()> cb, size_t stacksize)   called";
        ++s_fiber_count;
        m_stacksize = stacksize ? stacksize : g_fiber_stack_size->getValue();
        m_stack = StackAllocator::Alloc(m_stacksize);

        if (getcontext(&m_ctx))
        {
            SYLAR_ASSERT2(false, "Fiber::Fiber() getcontext()");
        }
        m_ctx.uc_link = nullptr;
        m_ctx.uc_stack.ss_sp = m_stack;
        m_ctx.uc_stack.ss_size = m_stacksize;

        if (!use_caller)
        {
            makecontext(&m_ctx, &Fiber::MainFunction, 0);
        }
        else
        {
            makecontext(&m_ctx, &Fiber::CallerMainFunction, 0);
        }

        SS() << "Fiber::Fiber(std::function<void()> cb, size_t stacksize)   end";
    }
    Fiber::~Fiber()
    {

        SS() << "Fiber::~Fiber()   called";
        --s_fiber_count;
        if (m_stack != nullptr)
        {
            SYLAR_ASSERT(m_state == TERM || m_state == INIT || m_state == EXCEPT);
            StackAllocator::Dealloc(m_stack, m_stacksize);
            SS() << "dealloc stack id = " << m_id;
        }
        else
        {
            SYLAR_ASSERT(!m_cb);
            SYLAR_ASSERT(m_state == EXEC);
            Fiber *cur = t_fiber;
            if (cur == this)
            {
                SetThis(nullptr);
            }
            SS() << "no staack id = " << m_id;
        }

        SS() << "Fiber::~Fiber()   end";
    }
    void Fiber::reset(std::function<void()> cb)
    {
        SYLAR_ASSERT(m_stack);
        SYLAR_ASSERT(m_state == TERM || m_state == INIT || m_state == EXCEPT);
        m_cb = cb;
        if (getcontext(&m_ctx))
        {
            SYLAR_ASSERT2(false, "Fiber::reset() getcontext() ");
        }
        m_ctx.uc_link = nullptr;
        m_ctx.uc_stack.ss_sp = m_stack;
        m_ctx.uc_stack.ss_size = m_stacksize;
        makecontext(&m_ctx, &Fiber::MainFunction, 0);
        m_state = INIT;
    }
    void Fiber::swapIn()
    {
        SetThis(this);
        SYLAR_ASSERT(m_state != EXEC);
        m_state = EXEC;
        if (swapcontext(&(Scheduler::GetMainFiber()->m_ctx), &m_ctx))
        {
            SYLAR_ASSERT2(false, "Fiber::swapIn() swapcontext()");
        }
    }
    void Fiber::swapOut()
    {
        SetThis(Scheduler::GetMainFiber());
        if (swapcontext(&m_ctx, &Scheduler::GetMainFiber()->m_ctx))
        {
            SYLAR_ASSERT2(false, "swapcontext");
        }
    }
    void Fiber::back()
    {
        SetThis(t_threadFiber.get());
        if (swapcontext(&m_ctx, &t_threadFiber->m_ctx))
        {
            SYLAR_ASSERT2(false, "swapcontext");
        }
    }
    Fiber::State Fiber::getState() const
    {
        return this->m_state;
    }
    void Fiber::SetThis(Fiber *f)
    {
        t_fiber = f;
    }
    Fiber::ptr Fiber::GetThis()
    {
        if (t_fiber)
        {
            return t_fiber->shared_from_this();
        }
        Fiber::ptr main_fiber(new Fiber());
        SYLAR_ASSERT(t_fiber == main_fiber.get());
        t_threadFiber = main_fiber;
        return t_fiber->shared_from_this();
    }
    void Fiber::YieldToReady()
    {
        Fiber::ptr cur = GetThis();
        
    SYLAR_ASSERT(cur->m_state == EXEC);
        cur->m_state = READY;
        cur->swapOut();
    }
    void Fiber::YieldToHold()
    {
        Fiber::ptr cur = GetThis();
        
    SYLAR_ASSERT(cur->m_state == EXEC);
        //cur->m_state = HOLD;
        cur->swapOut();
    }
    uint64_t Fiber::TotalFibers()
    {
        return s_fiber_count;
    }
    void Fiber::MainFunction()
    {
        SS() << "Fiber::MainFunction()";
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur);
        try
        {
            cur->m_cb();
            cur->m_cb = nullptr;
            cur->m_state = TERM;
            SS() << "Fiber::MainFunction() cb ok;";
        }
        catch (const std::exception &e)
        {
            cur->m_state = EXCEPT;

            SYLAR_LOG_ERROR(g_logger) << "Fiber Except:" << e.what()
                                      << " fiber_id= " << cur->getId()
                                      << std::endl
                                      << sylar::BackTraceToString();
        }
        catch (...)
        {
            cur->m_state = EXCEPT;

            SYLAR_LOG_ERROR(g_logger) << "Fiber Except: " << "unknown error"
                                      << " fiber_id= " << cur->getId()
                                      << std::endl
                                      << sylar::BackTraceToString();
        }
        auto raw_ptr = cur.get();
        cur.reset();

        SS() << "Fiber::MainFunction()  will end ::to swapOut()";
        raw_ptr->swapOut();
        SYLAR_ASSERT2(false, "you can't reach there   :error" + std::to_string(raw_ptr->getId()));
    }

    void Fiber::CallerMainFunction()
    {
        SS() << "Fiber::CallMainFunction()";
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur);
        try
        {
            cur->m_cb();
            cur->m_cb = nullptr;
            cur->m_state = TERM;
            SS() << "Fiber::CallMainFunction() cb ok;";
        }
        catch (const std::exception &e)
        {
            cur->m_state = EXCEPT;

            SYLAR_LOG_ERROR(g_logger) << "Fiber Except:" << e.what()
                                      << " fiber_id= " << cur->getId()
                                      << std::endl
                                      << sylar::BackTraceToString();
        }
        catch (...)
        {
            cur->m_state = EXCEPT;

            SYLAR_LOG_ERROR(g_logger) << "Fiber Except: " << "unknown error"
                                      << " fiber_id= " << cur->getId()
                                      << std::endl
                                      << sylar::BackTraceToString();
        }
        auto raw_ptr = cur.get();
        cur.reset();

        SS() << "Fiber::CallMainFunction()  will end ::to swapOut()";
        raw_ptr->back();
        SYLAR_ASSERT2(false, "you can't reach there   :error" + std::to_string(raw_ptr->getId()));
    }
    uint64_t Fiber::GetFiberId()
    {
        if (t_fiber)
        {
            return t_fiber->getId();
        }
        return 0;
    }
    void Fiber::call()
    {
        SetThis(this);
        m_state = EXEC;
        // SYLAR_ASSERT(GetThis().get() == t_threadFiber.get());
        if (swapcontext(&(t_threadFiber->m_ctx), &m_ctx))
        {
            SYLAR_ASSERT2(false, "Fiber::swapIn() swapcontext()");
        }
    }
}

//     class Fiber : public std::enable_shared_from_this<Fiber>
//     {
//     public:
//         typedef std::shared_ptr<Fiber> ptr;
//         enum State
//         {
//             INIT,
//             HOLD,
//             EXCEPT,
//             EXEC,
//             TERM,
//             READY
//         };
// // 状态	    英文含义	    中文解释	说明
// // INIT	    Initial	        初始状态	协程刚被创建，还未开始运行（构造函数执行完毕，但 m_cb 还没被调用）。
// // READY	Ready	        就绪状态	协程已经准备好运行，等待被调度器（scheduler）选中并切换到运行状态。
// // EXEC	    Executing	    运行状态	协程正在执行中（m_cb 函数正在运行）。
// // HOLD	    Holding     	暂停状态	协程主动暂停（比如调用 YieldToHold()），此时它让出 CPU 执行权，等待再次被唤醒。
// // EXCEPT	Exception	    异常状态	协程在执行过程中发生了未捕获的异常，导致状态变为异常。
// // TERM	    Terminated	    终止状态	协程的 m_cb 函数执行完毕，或者因异常退出，协程生命周期结束。
//     private:
//         Fiber();
//     public:
//         Fiber(std::function<void()> cb, size_t stacksize = 0);
//         ~Fiber();
//         void reset(std::function<void()> new_cb);
//         void call();
//         void swapIn();
//         void swapOut();
//         State getState()const ;
//         uint64_t getId() const
//         {
//             return m_id;
//         }

//     public:
//         static void SetThis(Fiber *f);
//         static Fiber::ptr GetThis();
//         static void YieldToReady();
//         static void YieldToHold();
//         static uint64_t TotalFibers();
//         static void MainFunction();

//         static uint64_t GetFiberId();

//     private:
//         uint64_t m_id;
//         uint32_t m_stacksize;
//         State m_state = INIT;
//         ucontext_t m_ctx;
//         void *m_stack = nullptr;
//         std::function<void()> m_cb;
//     };